use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{Display, format, Formatter};
use std::rc::Rc;
use std::time::SystemTime;
use crate::expr::{EvaluatedValue, EvaluateResult, Expression, Literal};
use crate::interpreter::{Environment, ExecErr, ExecResult, Interpreter};
use crate::scanner::{LiteralValue, Token};
use crate::stmt::{Stmt, StmtFunction};


pub trait LoxCallable {
    fn call(&self, interpreter: &mut Interpreter, arguments: &[EvaluatedValue]) -> EvaluateResult;
    fn arity(&self) -> usize;
}

#[derive(Debug, Clone)]
pub struct LoxFunction {
    pub declaration: StmtFunction,
    closure: Rc<RefCell<Environment>>,
    is_initializer: bool
}

impl LoxFunction {
    pub fn new(declaration: StmtFunction, closure: Rc<RefCell<Environment>>, is_initializer: bool) -> Self {
        Self {declaration, closure, is_initializer}
    }
}

impl LoxFunction {
    pub fn bind(&self, instance: Rc<RefCell<LoxInstance>>, is_initializer: bool) -> Self {
        let mut env = Environment::with_enclosing(self.closure.clone());
        env.define("this".to_string(), EvaluatedValue::Instance(instance));
        Self::new(self.declaration.clone(), Rc::new(RefCell::new(env)),  is_initializer)
    }
}


impl LoxCallable for LoxFunction {
    fn call(&self, interpreter: &mut Interpreter,  arguments: &[EvaluatedValue]) -> EvaluateResult {
        let mut env = Environment::with_enclosing(self.closure.clone());

        for i in 0..arguments.len() {
            env.define(self.declaration.params[i].lexeme.clone(), arguments[i].clone())
        }

        match interpreter.execute_block(&self.declaration.body, env) {
            Ok(v) => Ok(v),
            Err(e) => {
                match e {
                    ExecErr::Break | ExecErr::Continue => Err((format!("Unexpected return value: {:?}", e))),
                    ExecErr::Return(v) => {
                        if self.is_initializer {
                            match self.closure.borrow().get_at("this", 0) {
                                None => Err("Cannot find instance".to_string()),
                                Some(v) => Ok(v)
                            }
                        } else {
                            Ok(v)
                        }
                    }
                    ExecErr::Other(ee) => Err(ee)
                }
            }
        }
    }

    fn arity(&self) -> usize {
        self.declaration.params.len()
    }
}

/// 默认的读取Unix时间戳的函数
pub struct Clock {}

impl LoxCallable for Clock {
    fn call(&self, interpreter: &mut Interpreter, arguments: &[EvaluatedValue]) -> EvaluateResult {
        let now = SystemTime::now();
        let ts = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
        Ok(EvaluatedValue::new_number(ts.as_secs_f64()))
    }

    fn arity(&self) -> usize {
        0
    }
}


#[derive(Clone, Debug)]
pub struct LoxClass {
    pub name: String,
    methods: HashMap<String, Rc<LoxFunction>>,
    pub superclass: Option<Rc<LoxClass>>
}

impl LoxClass {
    pub fn new(name: &str, methods: HashMap<String, Rc<LoxFunction>>) -> Self {
        Self { name: name.to_string(), methods, superclass: None }
    }

    pub fn with_superclass(name: &str, methods: HashMap<String, Rc<LoxFunction>>, superclass: Rc<LoxClass>) -> Self {
        Self { name: name.to_string(), methods, superclass: Some(superclass) }
    }

    pub fn find_method(&self, name: &str) -> Option<Rc<LoxFunction>> {
        match self.methods.get(name).map(|f| f.clone()) {
            None => {
                match self.superclass {
                    None => None,
                    Some(ref cls) => cls.find_method(name)
                }
            }
            Some(m) => Some(m)
        }
    }
}

impl LoxCallable for LoxClass {
    fn call(&self, interpreter: &mut Interpreter, arguments: &[EvaluatedValue]) -> EvaluateResult {
        let instance = LoxInstance::new(self.clone());

        match self.find_method("init") {
            None => {}
            Some(ref i) => {
                i.bind(Rc::new(RefCell::new(instance.clone())), true).call(interpreter, arguments)?;
            }
        }

        Ok(EvaluatedValue::Instance(Rc::new(RefCell::new(instance))))
    }

    fn arity(&self) -> usize {
        match self.find_method("init") {
            None => 0,
            Some(f) => f.arity()
        }
    }
}


#[derive(Clone, Debug)]
pub struct LoxInstance {
    pub klass: LoxClass,
    fields: Rc<RefCell<HashMap<String, EvaluatedValue>>>
}

impl LoxInstance {
    pub fn new(klass: LoxClass) -> Self {
        Self { klass, fields: Rc::new(RefCell::new(HashMap::new())) }
    }

    pub fn get(&self, name: &Token) -> Option<EvaluatedValue> {
        match self.fields.borrow().get(&name.lexeme).map(|v| v.clone()) {
            None => {
                match self.klass.find_method(&name.lexeme) {
                    None => None,
                    Some(f) => Some(EvaluatedValue::Call(
                        Rc::new(f.bind(Rc::new(RefCell::new(self.clone())), name.lexeme == "init")))
                    )
                }
            }
            Some(v) => Some(v)
        }
    }

    pub fn set(&mut self, name: &Token, value: EvaluatedValue) {
        self.fields.borrow_mut().insert(name.lexeme.clone(), value);
    }
}

impl Display for LoxInstance {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} instance", self.klass.name)
    }
}